*/
-#include <cctype> // for isprint
-#include <cmath> // for fabs, lround
-#include <cstdio> // for snprintf, sscanf, NULL, fprintf, fputc, stderr
-#include <cstdlib> // for atoi, atof, strtod
-#include <cstring> // for strncmp, memset, strlen, strchr, strstr, strrchr
-#include <ctime> // for time_t, gmtime
-#include <iterator> // for operator!=, reverse_iterator
-
-#include <QtCore/QByteArray> // for QByteArray
-#include <QtCore/QChar> // for QChar, operator==, operator!=
-#include <QtCore/QCharRef> // for QCharRef
-#include <QtCore/QDateTime> // for QDateTime
-#include <QtCore/QDebug> // for QDebug
-#include <QtCore/QList> // for QList
-#include <QtCore/QString> // for QString, QString::KeepEmptyParts
-#include <QtCore/QStringList> // for QStringList
-#include <QtCore/QTextStream> // for hex
-#include <QtCore/QThread> // for QThread
-#include <QtCore/QTime> // for QTime
-#include <QtCore/QtGlobal> // for qPrintable, foreach
+#include <cctype> // for isprint
+#include <cmath> // for fabs
+#include <cstdio> // for snprintf, sscanf, fprintf, fputc, stderr
+#include <cstdlib> // for atoi, atof, strtod
+#include <cstring> // for strncmp, strchr, strlen, strstr, memset, strrchr
+#include <iterator> // for operator!=, reverse_iterator
+
+#include <QtCore/QByteArray> // for QByteArray
+#include <QtCore/QChar> // for QChar, operator==, operator!=
+#include <QtCore/QDateTime> // for QDateTime
+#include <QtCore/QDebug> // for QDebug
+#include <QtCore/QList> // for QList
+#include <QtCore/QString> // for QString, QString::KeepEmptyParts
+#include <QtCore/QStringList> // for QStringList
+#include <QtCore/QTextStream> // for hex
+#include <QtCore/QThread> // for QThread
+#include <QtCore/QTime> // for QTime
+#include <QtCore/Qt> // for UTC
+#include <QtCore/QtGlobal> // for qPrintable, foreach
#include "defs.h"
#include "nmea.h"
#include "jeeps/gpsmath.h" // for GPS_Lookup_Datum_Index, GPS_Math_Known_Datum_To_WGS84_M
#include "src/core/datetime.h" // for DateTime
#include "src/core/logging.h" // for Warning
-#include "strptime.h" // for strptime
/**********************************************************
{
curr_waypt = nullptr;
last_waypt = nullptr;
- last_time = -1;
datum = DATUM_WGS84;
- had_checksum = 0;
+ had_checksum = false;
CHECK_BOOL(opt_gprmc);
CHECK_BOOL(opt_gpgga);
file_out = gbfopen(fname, append_output ? "a+" : "w+", MYNAME);
- sleepus = -1;
+ sleepms = -1;
if (opt_sleep) {
if (*opt_sleep) {
- sleepus = 1e6 * atof(opt_sleep);
+ sleepms = 1e3 * atof(opt_sleep);
} else {
- sleepus = -1;
+ sleepms = -1;
}
}
}
void
-NmeaFormat::nmea_set_waypoint_time(Waypoint* wpt, struct tm* time, double fsec)
+NmeaFormat::nmea_set_waypoint_time(Waypoint* wpt, QDateTime* prev, const QDate& date, const QTime& time)
{
- if (time->tm_year == 0) {
- wpt->SetCreationTime(((((time_t)time->tm_hour * 60) + time->tm_min) * 60) + time->tm_sec, lround(1000.0 * fsec));
- if (wpt->wpt_flags.fmt_use == 0) {
- wpt->wpt_flags.fmt_use = 1;
- without_date++;
+ if (date.isValid()) {
+ wpt->SetCreationTime(QDateTime(date, time, Qt::UTC));
+ if (wpt->wpt_flags.fmt_use != 0) {
+ wpt->wpt_flags.fmt_use = 0;
+ without_date--;
+ }
+ *prev = wpt->GetCreationTime();
+ } else if (prev->date().isValid()) {
+ wpt->SetCreationTime(QDateTime(prev->date(), time, Qt::UTC));
+ if (*prev > wpt->creation_time) {
+ /* go over midnight ? */
+ wpt->creation_time = wpt->creation_time.addDays(1);
}
- } else {
- wpt->SetCreationTime(mkgmtime(time), lround(1000.0 * fsec));
if (wpt->wpt_flags.fmt_use != 0) {
wpt->wpt_flags.fmt_use = 0;
without_date--;
}
+ *prev = wpt->GetCreationTime();
+ } else {
+ wpt->SetCreationTime(QDateTime(QDate(), time, Qt::UTC));
+ if (wpt->wpt_flags.fmt_use == 0) {
+ wpt->wpt_flags.fmt_use = 1;
+ without_date++;
+ }
}
}
+QTime NmeaFormat::nmea_parse_hms(const QString& str)
+{
+ QString format = str.contains('.')? "hhmmss.zzz" : "hhmmss";
+ return QTime::fromString(str, format);
+}
+
void
-NmeaFormat::gpgll_parse(char* ibuf)
+NmeaFormat::gpgll_parse(const QString& ibuf)
{
if (trk_head == nullptr) {
trk_head = new route_head;
track_add_head(trk_head);
}
- QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
double latdeg = 0;
if (fields.size() > 1) latdeg = fields[1].toDouble();
if (fields.size() > 3) lngdeg = fields[3].toDouble();
QChar lngdir = 'E';
if (fields.size() > 4) lngdir = fields[4][0];
- double hmsd = 0;
- if (fields.size() > 5) hmsd = fields[5].toDouble();
+ QTime hms;
+ if (fields.size() > 5) hms = nmea_parse_hms(fields[5]);
bool valid = false;
if (fields.size() > 6) valid = fields[6].startsWith('A');
return;
}
- int hms = (int) hmsd;
last_read_time = hms;
- double fsec = hmsd - hms;
-
- tm.tm_sec = hms % 100;
- hms = hms / 100;
- tm.tm_min = hms % 100;
- hms = hms / 100;
- tm.tm_hour = hms % 100;
Waypoint* waypt = nmea_new_wpt();
- nmea_set_waypoint_time(waypt, &tm, fsec);
+ nmea_set_waypoint_time(waypt, &prev_datetime, QDate(), hms);
if (latdir == 'S') {
latdeg = -latdeg;
}
void
-NmeaFormat::gpgga_parse(char* ibuf)
+NmeaFormat::gpgga_parse(const QString& ibuf)
{
if (trk_head == nullptr) {
trk_head = new route_head;
track_add_head(trk_head);
}
- QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
- double hms = 0;
- if (fields.size() > 1) hms = fields[1].toDouble();
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
+ QTime hms;
+ if (fields.size() > 1) hms = nmea_parse_hms(fields[1]);
double latdeg = 0;
if (fields.size() > 2) latdeg = fields[2].toDouble();
QChar latdir = 'N';
if (fields.size() > 6) fix = fields[6].toInt();
int nsats = 0;
if (fields.size() > 7) nsats = fields[7].toInt();
- double hdop = 0;
- if (fields.size() > 8) hdop = fields[8].toDouble();
+ float hdop = 0;
+ if (fields.size() > 8) hdop = fields[8].toFloat();
double alt = unknown_alt;
if (fields.size() > 9) alt = fields[9].toDouble();
QChar altunits ='M';
}
last_read_time = hms;
- double fsec = hms - (int)hms;
-
- tm.tm_sec = (long) hms % 100;
- hms = hms / 100;
- tm.tm_min = (long) hms % 100;
- hms = hms / 100;
- tm.tm_hour = (long) hms % 100;
Waypoint* waypt = nmea_new_wpt();
- nmea_set_waypoint_time(waypt, &tm, fsec);
+ nmea_set_waypoint_time(waypt, &prev_datetime, QDate(), hms);
if (latdir == 'S') {
latdeg = -latdeg;
}
void
-NmeaFormat::gprmc_parse(char* ibuf)
+NmeaFormat::gprmc_parse(const QString& ibuf)
{
if (trk_head == nullptr) {
trk_head = new route_head;
track_add_head(trk_head);
}
- QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
- double hms = 0;
- if (fields.size() > 1) hms = fields[1].toDouble();
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
+ QTime hms;
+ if (fields.size() > 1) hms = nmea_parse_hms(fields[1]);
QChar fix = 'V'; // V == "Invalid"
if (fields.size() > 2) fix = fields[2][0];
double latdeg = 0;
if (fields.size() > 7) speed = fields[7].toDouble();
double course = 0;
if (fields.size() > 8) course = fields[8].toDouble();
- int dmy = 0;
- if (fields.size() > 9) dmy = fields[9].toDouble();
-
+ QDate dmy;
+ if (fields.size() > 9) {
+ QString datestr(fields[9]);
+ datestr.insert(4, "20");
+ dmy = QDate::fromString(datestr, "ddMMyyyy");
+ }
if (fix != 'A') {
/* ignore this fix - it is invalid */
return;
}
last_read_time = hms;
- double fsec = hms - (int)hms;
-
- tm.tm_sec = (long) hms % 100;
- hms = hms / 100;
- tm.tm_min = (long) hms % 100;
- hms = hms / 100;
- tm.tm_hour = (long) hms % 100;
-
- tm.tm_year = dmy % 100 + 100;
- dmy = dmy / 100;
- tm.tm_mon = dmy % 100 - 1;
- dmy = dmy / 100;
- tm.tm_mday = dmy;
if (posn_type == gpgga) {
/* capture useful data update and exit */
}
/* The change of date wasn't recorded when
* going from 235959 to 000000. */
- nmea_set_waypoint_time(curr_waypt, &tm, fsec);
+ nmea_set_waypoint_time(curr_waypt, &prev_datetime, dmy, hms);
}
/* This point is both a waypoint and a trackpoint. */
if (amod_waypoint) {
WAYPT_SET(waypt, speed, KNOTS_TO_MPS(speed));
WAYPT_SET(waypt, course, course);
- nmea_set_waypoint_time(waypt, &tm, fsec);
+ nmea_set_waypoint_time(waypt, &prev_datetime, dmy, hms);
if (latdir == 'S') {
latdeg = -latdeg;
}
void
-NmeaFormat::gpwpl_parse(char* ibuf)
+NmeaFormat::gpwpl_parse(const QString& ibuf)
{
- // The last field isn't actually separated by a field separator and
- // is a string, so we brutally whack the checksum (trailing *NN).
- QString qibuf = QString(ibuf);
- qibuf.truncate(qibuf.lastIndexOf('*'));
- QStringList fields = qibuf.split(",", QString::KeepEmptyParts);
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
double latdeg = 0;
if (fields.size() > 1) latdeg = fields[1].toDouble();
}
void
-NmeaFormat::gpzda_parse(char* ibuf)
+NmeaFormat::gpzda_parse(const QString& ibuf)
{
- double hms;
- int dd, mm, yy, lclhrs, lclmins;
-
- sscanf(ibuf,"$%*2cZDA,%lf,%d,%d,%d,%d,%d",
- &hms, &dd, &mm, &yy, &lclhrs, &lclmins);
- tm.tm_sec = (int) hms % 100;
- tm.tm_min = (((int) hms - tm.tm_sec) / 100) % 100;
- tm.tm_hour = (int) hms / 10000;
- tm.tm_mday = dd;
- tm.tm_mon = mm - 1;
- tm.tm_year = yy - 1900;
- // FIXME: why do we do all this and then do nothing with the result?
- // This can't have worked.
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
+ if (fields.size() > 4) {
+ QTime time = nmea_parse_hms(fields[1]);
+ QString datestr = QString("%1%2%3").arg(fields[2], fields[3], fields[4]);
+ QDate date = QDate::fromString(datestr, "ddMMyyyy");
+
+ // The prev_datetime data member might be used by
+ // nmea_fix_timestamps and nmea_set_waypoint_time.
+ prev_datetime = QDateTime(date, time, Qt::UTC);
+ }
}
// This function has had a hard life. It began as a moderately terrifying
// The numbering as per http://aprs.gids.nl/nmea/#gsa was the reference as
// the field numbers conveniently match our index.
void
-NmeaFormat::gpgsa_parse(char* ibuf) const
+NmeaFormat::gpgsa_parse(const QString& ibuf) const
{
int prn[12] = {0};
memset(prn,0xff,sizeof(prn));
- QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
int nfields = fields.size();
// 0 = "GPGSA"
// 1 = Mode. Ignored
float pdop = 0, hdop = 0, vdop = 0;
if (nfields > 15) pdop = fields[15].toFloat();
if (nfields > 16) hdop = fields[16].toFloat();
- if (nfields > 17) {
- // Last one is special. The checksum isn't split out above.
- fields[17].chop(3);
- vdop = fields[17].toFloat();
- }
+ if (nfields > 17) vdop = fields[17].toFloat();
if (curr_waypt) {
if (curr_waypt->fix!=fix_dgps) {
}
void
-NmeaFormat::gpvtg_parse(char* ibuf) const
+NmeaFormat::gpvtg_parse(const QString& ibuf) const
{
- QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
+ const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
double course = 0;
if (fields.size() > 1) course = fields[1].toDouble();
double speed_n = 0;
}
void
-NmeaFormat::pcmpt_parse(char* ibuf)
+NmeaFormat::pcmpt_parse(const char* ibuf)
{
int i, j1, j2, j3, j4, j5, j6;
int lat, lon;
curr_waypt->longitude = pcmpt_deg(lon);
curr_waypt->latitude = pcmpt_deg(lat);
- tm.tm_sec = (long) hms % 100;
+ int sec = hms % 100;
hms = hms / 100;
- tm.tm_min = (long) hms % 100;
+ int min = hms % 100;
hms = hms / 100;
- tm.tm_hour = (long) hms % 100;
+ int hour = hms % 100;
+ QTime time = QTime(hour, min, sec);
- tm.tm_year = dmy % 10000 - 1900;
+ int year = dmy % 10000;
dmy = dmy / 10000;
- tm.tm_mon = dmy % 100 - 1;
+ int mon = dmy % 100;
dmy = dmy / 100;
- tm.tm_mday = dmy;
- nmea_set_waypoint_time(curr_waypt, &tm, 0);
+ int mday = dmy;
+ QDate date = QDate(year, mon, mday);
+
+ nmea_set_waypoint_time(curr_waypt, &prev_datetime, date, time);
pcmpt_head.prepend(curr_waypt);
} else {
return;
}
- if (tm.tm_year == 0) {
- Waypoint* prev = nullptr;
-
+ if (!prev_datetime.date().isValid()) {
if (optdate == nullptr) {
warning(MYNAME ": No date found within track (all points dropped)!\n");
warning(MYNAME ": Please use option \"date\" to preset a valid date for those tracks.\n");
track_del_head(track);
return;
}
- time_t delta_tm = mkgmtime(&opt_tm);
+
+ QDateTime prev = QDateTime(opt_tm, QTime(0, 0), Qt::UTC);
foreach (Waypoint* wpt, track->waypoint_list) {
- wpt->creation_time = wpt->creation_time.addSecs(delta_tm);
- if ((prev != nullptr) && (prev->creation_time > wpt->creation_time)) {
+ wpt->creation_time.setDate(prev.date());
+ if (prev > wpt->creation_time) {
/* go over midnight ? */
- delta_tm += SECONDS_PER_DAY;
- wpt->creation_time = wpt->creation_time.addSecs(SECONDS_PER_DAY);
+ wpt->creation_time = wpt->creation_time.addDays(1);
}
- prev = wpt;
+ prev = wpt->GetCreationTime();
}
} else {
- time_t prev = mkgmtime(&tm);
+ QDateTime prev = prev_datetime;
/* go backward through the track and complete timestamps */
if (wpt->wpt_flags.fmt_use != 0) {
wpt->wpt_flags.fmt_use = 0; /* reset flag */
- time_t dt = (prev / SECONDS_PER_DAY) * SECONDS_PER_DAY;
- wpt->creation_time = wpt->creation_time.addSecs(dt);
- if (wpt->creation_time.toTime_t() > prev) {
- wpt->creation_time = wpt->creation_time.addSecs(-SECONDS_PER_DAY);
+ wpt->creation_time.setDate(prev.date());
+ if (wpt->creation_time > prev) {
+ wpt->creation_time = wpt->creation_time.addDays(-1);
}
}
- prev = wpt->GetCreationTime().toTime_t();
+ prev = wpt->GetCreationTime();
}
}
}
-int
-NmeaFormat::notalkerid_strmatch(const char * s1, const char *sentenceFormatterMnemonicCode)
+bool
+NmeaFormat::notalkerid_strmatch(const QByteArray& s1, const char *sentenceFormatterMnemonicCode)
{
/*
- * compare leading start of parametric sentence character ('$'), sentence address field, and trailing comma
- * to the desired sentence formatter mnemonic code (the 3rd-5th characters of the sentence address field).
- * The talker identifier mnemonic(the 1st-2nd characters of the sentence address field)
- * is likely "GP" for Global Positioning System (GPS)
- * but other talkers like "IN" for Integrated Navigation can emit relevant sentences,
- * so we ignore the talker identifier mnemonic.
+ * compare leading start of parametric sentence character ('$'), sentence
+ * address field, and trailing comma to the desired sentence formatter mnemonic
+ * code (the 3rd-5th characters of the sentence address field). The talker
+ * identifier mnemonic (the 1st-2nd characters of the sentence address field)
+ * is likely "GP" for Global Positioning System (GPS) but other talkers like
+ * "IN" for Integrated Navigation can emit relevant sentences, so we ignore the
+ * talker identifier mnemonic.
*/
-return strncmp(s1,"$",1) || strncmp(s1+3,sentenceFormatterMnemonicCode,3) || strncmp(s1+6,",",1);
+ return (s1.size() > 6) && (s1.at(0) == '$') && (s1.at(6) == ',') &&
+ (s1.mid(3,3) == sentenceFormatterMnemonicCode);
}
void
-NmeaFormat::nmea_parse_one_line(char* ibuf)
+NmeaFormat::nmea_parse_one_line(const QByteArray& ibuf)
{
- char* tbuf = lrtrim(ibuf);
+ QByteArray tbuf = ibuf.trimmed();
/*
* GISTEQ PhotoTracker (stupidly) puts a bogus field in front
* of the line. Look for it and toss it.
*/
- if (0 == strncmp(tbuf, "---,", 4)) {
- tbuf += 4;
+ if (tbuf.startsWith("---,")) {
+ tbuf.remove(0, 4);
}
- if (*tbuf != '$') {
+ if (tbuf.at(0) != '$') {
return;
}
- char* ck = strrchr(tbuf, '*');
- if (ck != nullptr) {
- *ck = '\0';
- int ckval = nmea_cksum(&tbuf[1]);
- *ck = '*';
- ck++;
- int ckcmp;
- sscanf(ck, "%2X", &ckcmp);
- if (ckval != ckcmp) {
- Warning().nospace() << hex << "Invalid NMEA checksum. Computed 0x" << ckval << " but found 0x" << ckcmp << ". Ignoring sentence.";
+ if (int ckidx = tbuf.lastIndexOf('*'); ckidx >= 0) {
+ bool checked = false;
+ if ((ckidx + 2) < tbuf.size()) {
+ QByteArray ckstring = tbuf.mid(ckidx + 1, 2);
+ bool ok;
+ int ckcmp = ckstring.toInt(&ok, 16);
+ if (ok) {
+ int ckval = nmea_cksum(tbuf.mid(1, ckidx - 1));
+ if (ckval != ckcmp) {
+ Warning().nospace() << hex << "Invalid NMEA checksum. Computed 0x" << ckval << " but found 0x" << ckcmp << ". Ignoring sentence.";
+ return;
+ }
+ checked = true;
+ }
+ }
+ if (!checked) {
+ Warning().nospace() << "Unrecoverable NMEA checksum in line " << tbuf << ". Ignoring sentence.";
return;
}
-
- had_checksum = 1;
+ // hide checksum from sentence parsers.
+ tbuf.truncate(ckidx);
+ had_checksum = true;
} else if (had_checksum) {
/* we have had a checksum on all previous sentences, but not on this
one, which probably indicates this line is truncated */
- had_checksum = 0;
+ had_checksum = false;
return;
}
- if (strstr(tbuf+1,"$")!=nullptr) {
+ if (tbuf.count("$") > 1) {
/* If line has more than one $, there is probably an error in it. */
return;
}
for that field. Rather than change all the parse routines, we first
substitute a default value of zero for any missing field.
*/
- if (strstr(tbuf, ",,")) {
- tbuf = gstrsub(tbuf, ",,", ",0,");
- }
+ tbuf.replace(",,", ",0,");
- if (0 == notalkerid_strmatch(tbuf, "WPL")) {
+ if (notalkerid_strmatch(tbuf, "WPL")) {
gpwpl_parse(tbuf);
- } else if (opt_gpgga && (0 == notalkerid_strmatch(tbuf, "GGA"))) {
+ } else if (opt_gpgga && notalkerid_strmatch(tbuf, "GGA")) {
posn_type = gpgga;
gpgga_parse(tbuf);
- } else if (opt_gprmc && (0 == notalkerid_strmatch(tbuf, "RMC"))) {
+ } else if (opt_gprmc && notalkerid_strmatch(tbuf, "RMC")) {
if (posn_type != gpgga) {
posn_type = gprmc;
}
* it contains the full date.
*/
gprmc_parse(tbuf);
- } else if (0 == notalkerid_strmatch(tbuf, "GLL")) {
+ } else if (notalkerid_strmatch(tbuf, "GLL")) {
if ((posn_type != gpgga) && (posn_type != gprmc)) {
gpgll_parse(tbuf);
}
- } else if (0 == notalkerid_strmatch(tbuf, "ZDA")) {
+ } else if (notalkerid_strmatch(tbuf, "ZDA")) {
gpzda_parse(tbuf);
- } else if (0 == strncmp(tbuf, "$PCMPT,", 7)) {
- pcmpt_parse(tbuf);
- } else if (opt_gpvtg && (0 == notalkerid_strmatch(tbuf, "VTG"))) {
+ } else if (tbuf.startsWith("$PCMPT,")) {
+ pcmpt_parse(tbuf.constData());
+ } else if (opt_gpvtg && notalkerid_strmatch(tbuf, "VTG")) {
gpvtg_parse(tbuf); /* speed and course */
- } else if (opt_gpgsa && (0 == notalkerid_strmatch(tbuf, "GSA"))) {
+ } else if (opt_gpgsa && notalkerid_strmatch(tbuf, "GSA")) {
gpgsa_parse(tbuf); /* GPS fix */
- } else if (0 == strncmp(tbuf, "$ADPMB,5,0", 10)) {
+ } else if (tbuf.startsWith("$ADPMB,5,0")) {
amod_waypoint = true;
}
-
- if (tbuf != ibuf) {
- /* clear up the dynamic buffer we used because substitution was required */
- xfree(tbuf);
- }
}
void
NmeaFormat::read()
{
char* ibuf;
- double lt = -1;
+ QTime lt;
int line = -1;
posn_type = gp_unknown;
trk_head = nullptr;
without_date = 0;
- memset(&tm, 0, sizeof(tm));
- opt_tm = tm;
+ prev_datetime = QDateTime();
+ opt_tm = QDate();
/* This was done in rd_init() */
if (getposn) {
}
if (optdate) {
- memset(&opt_tm, 0, sizeof(opt_tm));
-
- char* ck = strptime(optdate, "%Y%m%d", &opt_tm);
- if ((ck == nullptr) || (*ck != '\0') || (strlen(optdate) != 8)) {
+ opt_tm = QDate::fromString(optdate, "yyyyMMdd");
+ if (!opt_tm.isValid()) {
fatal(MYNAME ": Invalid date \"%s\"!\n", optdate);
- } else if (opt_tm.tm_year < 70) {
- fatal(MYNAME ": Date \"%s\" is out of range (have to be 19700101 or later)!\n", optdate);
}
}
NmeaFormat::rd_position(posn_status* /*unused*/)
{
char ibuf[1024];
- static double lt = -1;
+ static QTime lt;
int am_sirf = 0;
/*
}
nmea_parse_one_line(ibuf);
if (lt != last_read_time) {
- if (last_read_time) {
+ if (last_read_time.isValid()) {
Waypoint* w = curr_waypt;
lt = last_read_time;
);
int cksum = nmea_cksum(obuf);
gbfprintf(file_out, "$%s*%02X\n", obuf, cksum);
- if (sleepus >= 0) {
+ if (sleepms >= 0) {
gbfflush(file_out);
- QThread::usleep(sleepus);
+ QThread::msleep(sleepms);
}
}
void
NmeaFormat::nmea_track_init(const route_head* /*unused*/)
{
- last_time = -1;
+ first_trkpt = true;
}
void
if (opt_sleep) {
gbfflush(file_out);
- if (last_time > 0) {
- if (sleepus >= 0) {
- QThread::usleep(sleepus);
+ if (!first_trkpt) {
+ if (sleepms >= 0) {
+ QThread::msleep(sleepms);
} else {
- long wait_time = wpt->GetCreationTime().toTime_t() - last_time;
+ // wait_time will be 0 if either creation time is invalid.
+ long wait_time = last_write_time.msecsTo(wpt->GetCreationTime());
if (wait_time > 0) {
- QThread::usleep(wait_time * 1000000);
+ QThread::msleep(wait_time);
}
}
}
- last_time = wpt->GetCreationTime().toTime_t();
+ last_write_time = wpt->GetCreationTime();
+ first_trkpt = false;
}
double lat = degrees2ddmm(wpt->latitude);
pkt[27] = br & 0xff;
sirf_write(pkt);
- QThread::usleep(250 * 1000);
+ QThread::msleep(250);
gbser_flush(gbser_handle);
}
#ifndef NMEA_H_INCLUDED_
#define NMEA_H_INCLUDED_
-#include <ctime> // for gmtime
-
-#include <QtCore/QList> // for QList
-#include <QtCore/QString> // for QString, QString::KeepEmptyParts
-#include <QtCore/QVector> // for QVector
+#include <QtCore/QByteArray> // for QByteArray
+#include <QtCore/QDate> // for QDate
+#include <QtCore/QDateTime> // for QDateTime
+#include <QtCore/QList> // for QList
+#include <QtCore/QString> // for QString
+#include <QtCore/QTime> // for QTime
+#include <QtCore/QVector> // for QVector
#include "defs.h"
-#include "format.h"
-#include "gbfile.h" // for gbfprintf, gbfflush, gbfclose, gbfopen, gbfgetstr, gbfile
+#include "format.h" // for Format
+#include "gbfile.h" // for gbfile
class NmeaFormat : public Format
Waypoint* nmea_new_wpt();
void nmea_add_wpt(Waypoint* wpt, route_head* trk) const;
static void nmea_release_wpt(Waypoint* wpt);
- void nmea_set_waypoint_time(Waypoint* wpt, tm* time, double fsec);
- void gpgll_parse(char* ibuf);
- void gpgga_parse(char* ibuf);
- void gprmc_parse(char* ibuf);
- void gpwpl_parse(char* ibuf);
- void gpzda_parse(char* ibuf);
- void gpgsa_parse(char* ibuf) const;
- void gpvtg_parse(char* ibuf) const;
+ void nmea_set_waypoint_time(Waypoint* wpt, QDateTime* prev, const QDate& date, const QTime& time);
+ static QTime nmea_parse_hms(const QString& str);
+ void gpgll_parse(const QString& ibuf);
+ void gpgga_parse(const QString& ibuf);
+ void gprmc_parse(const QString& ibuf);
+ void gpwpl_parse(const QString& ibuf);
+ void gpzda_parse(const QString& ibuf);
+ void gpgsa_parse(const QString& ibuf) const;
+ void gpvtg_parse(const QString& ibuf) const;
static double pcmpt_deg(int d);
- void pcmpt_parse(char* ibuf);
+ void pcmpt_parse(const char* ibuf);
void nmea_fix_timestamps(route_head* track);
- static int notalkerid_strmatch(const char* s1, const char* sentenceFormatterMnemonicCode);
- void nmea_parse_one_line(char* ibuf);
+ static bool notalkerid_strmatch(const QByteArray& s1, const char* sentenceFormatterMnemonicCode);
+ void nmea_parse_one_line(const QByteArray& ibuf);
static void safe_print(int cnt, const char* b);
int hunt_sirf();
void nmea_wayptpr(const Waypoint* wpt) const;
route_head* trk_head{};
short_handle mkshort_handle{};
preferred_posn_type posn_type;
- struct tm tm{};
+ QDateTime prev_datetime;
Waypoint* curr_waypt{};
Waypoint* last_waypt{};
void* gbser_handle{};
QList<Waypoint*> pcmpt_head;
int without_date{}; /* number of created trackpoints without a valid date */
- struct tm opt_tm{}; /* converted "date" parameter */
+ QDate opt_tm; /* converted "date" parameter */
char* opt_gprmc{};
char* opt_gpgga{};
char* opt_gisteq{};
char* opt_ignorefix{};
- long sleepus{};
+ long sleepms{};
int getposn{};
int append_output{};
bool amod_waypoint{};
- time_t last_time{};
- double last_read_time{}; /* Last timestamp of GGA or PRMC */
+ QDateTime last_write_time;
+ bool first_trkpt{};
+ QTime last_read_time; /* Last timestamp of GGL, GGA or RMC */
int datum{};
- int had_checksum{};
-
- Waypoint* nmea_rd_posn(posn_status*);
- void nmea_rd_posn_init(const QString& fname);
+ bool had_checksum{};
int wpt_not_added_yet{};
--- /dev/null
+No,Latitude,Longitude,Altitude,FIX,HDOP,Satellites,Date,Time\r
+1,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/08,22:00:00\r
+2,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/08,23:00:00\r
+3,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,00:00:00\r
+4,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,01:00:00\r
+5,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,02:00:00\r
+6,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,03:00:00\r
+7,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,04:00:00\r
+8,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,05:00:00\r
+9,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,06:00:00\r
+10,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,07:00:00\r
+11,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,08:00:00\r
+12,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,09:00:00\r
+13,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,10:00:00\r
+14,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,11:00:00\r
+15,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,12:00:00\r
+16,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,13:00:00\r
+17,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,14:00:00\r
+18,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,15:00:00\r
+19,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,16:00:00\r
+20,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,17:00:00\r
+21,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,18:00:00\r
+22,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,19:00:00\r
+23,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,20:00:00\r
+24,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,21:00:00\r
+25,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,22:00:00\r
+26,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,23:00:00\r
+27,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/10,00:00:00\r
+28,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/10,01:00:00\r
--- /dev/null
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,020000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,030000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,040000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,050000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,060000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,070000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,080000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,090000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,100000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,110000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,120000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,130000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,140000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,150000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,160000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,170000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,180000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,190000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,200000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,210000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPZDA,033141,10,03,2019,00,00*44
--- /dev/null
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,020000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,030000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,040000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,050000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,060000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,070000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,080000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,090000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,100000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,110000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,120000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,130000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,140000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,150000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,160000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,170000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,180000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,190000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,200000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,210000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
--- /dev/null
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,020000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,030000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,040000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,050000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,060000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,070000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,080000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,090000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,100000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPZDA,103000,09,03,2019,00,00*4a
+$GPGGA,110000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,120000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,130000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,140000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,150000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,160000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,170000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,180000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,190000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,200000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,210000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
gpsbabel -i gpx -f ${REFERENCE}/track/nmeadate.gpx -o nmea -F ${TMPDIR}/nmeadate.nmea
compare ${REFERENCE}/track/nmeadate.nmea ${TMPDIR}/nmeadate.nmea
+
+
+gpsbabel -t -i nmea,date=20190308 -f ${REFERENCE}/track/addmultipledaysfwd.nmea -o unicsv,utc=0 -F ${TMPDIR}/addmultipledaysfwd.csv
+compare ${REFERENCE}/track/addmultipledays.csv ${TMPDIR}/addmultipledaysfwd.csv
+gpsbabel -t -i nmea -f ${REFERENCE}/track/addmultipledaysbkw.nmea -o unicsv,utc=0 -F ${TMPDIR}/addmultipledaysbkw.csv
+compare ${REFERENCE}/track/addmultipledays.csv ${TMPDIR}/addmultipledaysbkw.csv
+gpsbabel -t -i nmea -f ${REFERENCE}/track/addmultipledaysmid.nmea -o unicsv,utc=0 -F ${TMPDIR}/addmultipledaysmid.csv
+compare ${REFERENCE}/track/addmultipledays.csv ${TMPDIR}/addmultipledaysmid.csv
pause between trackpoints.)
</para>
<para>
-If this option is specified without a value, the time between adjacent
+If this option is specified with a negative value, the time between adjacent
trackpoints will be computed and used for the length of the pause. That is,
if your trackpoints are 5 seconds apart, GPSBabel will pause 5 seconds
between trackpoints.